#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/input.h>

#define DEV_NAME "key_input"

#define GPFCON 0x56000050
#define GPFDAT 0x56000054
#define GPFUDP 0x56000058

volatile unsigned long * gpfcon = NULL;
volatile unsigned long * gpfdat = NULL;
volatile unsigned long * gpfupd = NULL;

struct key_des
{
    int irq;
    int number;
    unsigned long flags;
    char * name;
};

static struct key_des key_irqs[] = {
    {IRQ_EINT0, 0, IRQ_TYPE_EDGE_BOTH, "KEY1"},
    {IRQ_EINT1, 1, IRQ_TYPE_EDGE_BOTH, "KEY2"},
    {IRQ_EINT2, 2, IRQ_TYPE_EDGE_BOTH, "KEY3"},
    {IRQ_EINT3, 3, IRQ_TYPE_EDGE_BOTH, "KEY4"},
    {IRQ_EINT4, 4, IRQ_TYPE_EDGE_BOTH, "KEY5"},
};

static struct key_des * key_event;
static struct input_dev * key_input_dev;
static struct timer_list key_timer;

void key_timer_func(unsigned long arg)
{
    int number = key_event->number;
    int code = -1;
    int value;

    value = (ioread32)(gpfdat) & (1 << number) ? 1:0;

    switch(number) {
    case 0:
        code = KEY_ENTER;
        break;
    case 1:
        code = KEY_RIGHT;
        break;
    case 2:
        code = KEY_LEFT;
        break;
    case 3:
        code = KEY_DOWN;
        break;
    case 4:
        code = KEY_UP;
        break;
    }

    input_event(key_input_dev, EV_KEY, code, value);
    input_sync(key_input_dev);
}

static irqreturn_t key_interrupt_func(int irq, void * dev_id)
{
    key_event = (struct key_des *) dev_id;
    /* HZ就是一秒，除以100就是100ms */
    mod_timer(&key_timer, jiffies + HZ/50);

    return IRQ_RETVAL(IRQ_HANDLED);
}

static int key_init(void)
{
    int i;
    int error;

    key_input_dev = input_allocate_device();
    if (key_input_dev == NULL) {
        printk(KERN_ERR "key_input.c:Not enough memory\n");
        return -ENOMEM;
    }

    set_bit(EV_KEY, key_input_dev->evbit); // 产生按键事件

    set_bit(KEY_UP, key_input_dev->keybit);
    set_bit(KEY_DOWN, key_input_dev->keybit);
    set_bit(KEY_LEFT, key_input_dev->keybit);
    set_bit(KEY_RIGHT, key_input_dev->keybit);
    set_bit(KEY_ENTER, key_input_dev->keybit);

    error = input_register_device(key_input_dev);
    if (error) 
        printk(KERN_ERR "key_input.c:input_register_device error\n");

    init_timer(&key_timer);
    key_timer.function = &key_timer_func;
    add_timer(&key_timer);

    for (i = 0; i < sizeof(key_irqs) / sizeof(key_irqs[0]); i++) {
        error = request_irq(key_irqs[i].irq,
                            key_interrupt_func,
                            key_irqs[i].flags,
                            key_irqs[i].name,
                            (void *) &key_irqs[i]);
    }

    gpfcon = (volatile unsigned long *) ioremap(GPFCON, 4);
    gpfdat = (volatile unsigned long *) ioremap(GPFDAT, 4);
    gpfupd = (volatile unsigned long *) ioremap(GPFUDP, 4);

    *gpfupd &= ~( (3 << 0) | (3 << 2) | (3 << 4) | (3 << 6) | (3 << 8) );
    *gpfupd |= ( (2 << 0) | (2 << 2) | (2 << 4) | (2 << 6) | (2 << 8) );

    return 0;
}

static void key_exit(void)
{
    int i;

    for (i = 0; i < sizeof(key_irqs) / sizeof(key_irqs[0]); i++) 
        free_irq(key_irqs[i].irq, (void *) &key_irqs[i]);

    iounmap(gpfcon);
    iounmap(gpfdat);
    ionumap(gpfupd);

    del_timer(&key_timer);
    input_unregister_device(key_input_dev);
    input_free_device(key_input_dev);
}

module_init(key_init);
module_exit(key_exit);

MODULE_LICENSE("GPL");

